package handler

// func newProxyHandler() {

// }

// package handler

import (
	"crypto/tls"
	"encoding/json"
	"fmt"
	"io"
	"iptv_server/v3/v2_src/handler/middleware"
	"iptv_server/v3/v2_src/handler/proxyhelper"
	"iptv_server/v3/v2_src/utils"
	"iptv_server/v3/v2_src/utils/common"
	parserdecoder "iptv_server/v3/v2_src/utils/parser_decoder"

	"net/http"
	"net/url"
	"strings"
	"time"
)

const (
	proxyPath = "/api/proxy"
)

var (
	fallbackProxy            *proxyhelper.Fallback_redirect2fileServer
	defaultDecoderBuilderKey = "txt"
)

func initProxy(mux *http.ServeMux, middles ...middleware.MiddleWare) {
	h := middleware.ApplyMiddles(&proxy{
		tr: &http.Transport{
			TLSClientConfig: &tls.Config{
				InsecureSkipVerify: true,
			},
		},
	}, middles...)

	fallbackProxy = proxyhelper.New_fallback_redirect2fileServer()

	// parsers = make(map[string]parser.ParserBuilder)
	// parsers["m3u"] = parser.NewM3U
	// parsers["m3u"]=&

	// http.DefaultServeMux.Handle()

	// 这个不是代理, 是读取参数 代请求.
	mux.Handle(proxyPath, h)
	mux.Handle(proxyPath+"/", h)

	mux.HandleFunc("/api/proxy_clear_cache", func(w http.ResponseWriter, r *http.Request) {
		proxyStringCache.Clear()
		http.Get("http://127.0.0.1:19999/tv/clearcache")
	})

}

// var parsers map[string]parser.ParserBuilder

type proxy struct {
	tr *http.Transport
}

// "/api/proxy/"
// "0:/1:api/2:proxy/3:"
func (p *proxy) ServeHTTP(w http.ResponseWriter, r *http.Request) {

	// if strings.HasPrefix(r.URL.Path, "/gc/") {
	// 	u = "https://gcalic.v.myalicdn.com" + r.URL.Path
	// } else {
	_ = r.ParseForm()
	u := r.Form.Get("url")

	if len(u) <= 0 {
		return
	}
	u = strings.TrimRight(u, "\n")
	u = strings.TrimRight(u, "\r")
	parseKey := r.Form.Get("parser")
	decodeKey := r.Form.Get("decoder")

	ua := r.Form.Get("ua")
	headers := r.Form.Get("header")

	hasSorterKey := r.Form.Has("sorter")
	hasFilterKey := r.Form.Has("filter")

	hasCacheKey := r.Form.Has("cache")

	hasKeepKey := r.Form.Has("keep")
	history := "" // 以过期 但仍 keep着的数据
	save := func(key, v string) {
		if hasKeepKey {
			proxyStringCache.SaveWithKeep(key, v, true)
			return
		}
		proxyStringCache.Save(key, v)
	}

	// var headersKV map[string]string
	headersKV := make(map[string]string)
	// 设置 User-Agent
	if len(ua) > 0 {
		headersKV["User-Agent"] = ua
	}

	if len(headers) > 0 {
		// kv := make(map[string]string)
		if err := json.Unmarshal([]byte(headers), &headersKV); err == nil {
		}
	}
	// add: 新增http header: Range
	h_range := r.Header.Get("Range")
	if len(h_range) > 0 {
		headersKV["Range"] = h_range
	}

	// }

	// fmt.Printf("parseKey: %v\n", parseKey)
	// fmt.Printf("decodeKey: %v\n", decodeKey)
	// fmt.Printf("hasSorterKey: %v\n", hasSorterKey)
	// fmt.Printf("hasFilterKey: %v\n", hasFilterKey)

	utils.Logger.Infof("%s method=proxy.GET, url=%s", r.RemoteAddr, u)
	if len(parseKey) > 0 || hasCacheKey {
		if len(u) == 0 {
			u = r.Form.Get("url1") // key== "url" || "url1"
			if len(u) == 0 {
				return
			}
		}

		// use Cache, 不能所有都 cache ,比如 stream !
		// // 只有 parse 过的才使用cache
		if v, expired, ok := proxyStringCache.Get(r.URL.String()); ok {
			if !expired {
				utils.Logger.Infof("cache hit : %s, len=%d method=proxy.GET, url=%s", r.RemoteAddr, len(v), r.URL.String())
				io.WriteString(w, v)
				return
			}
			if hasKeepKey {
				history = v
			}

		}

		if hasCacheKey {
			resp, _ := p.get(w, nil, time.Second*30, u, headersKV)
			if resp == nil {
				return
			}
			defer resp.Body.Close()
			bs, err := io.ReadAll(resp.Body)
			if err != nil {
				return
			}

			if len(bs) > 0 && len(bs) < 1024*1024*256 {
				// cache: size在这个范围内的
				save(r.URL.String(), string(bs))
				// proxyStringCache.Save(r.URL.String(), string(bs))
			}
			w.Write(bs)
			return
		}
	}

	////已经 确认了, 自动重定向!
	// 但是重定向 之后  resp code==200而不是30x
	// 末尾 有获取 301的方法

	// u2 := resp.Request.URL.String()

	// for i := range 3 { // 最多 重定向3次, 需要手动处理 重定向吗?!还是会自动处理掉
	// 	utils.Logger.Infof("%d proxy StatusCode=%v", i, resp.StatusCode)
	// 	if resp.StatusCode == http.StatusMovedPermanently || resp.StatusCode == http.StatusFound {
	// 		u = resp.Header.Get("Location")
	// 		resp, err = client.Get(u)
	// 		if err != nil {
	// 			utils.Logger.Debugf("proxy err:%v", err)
	// 			w.WriteHeader(http.StatusNotFound)
	// 			return
	// 		}
	// 	} else {
	// 		break
	// 	}
	// }

	if len(parseKey) > 0 {
		resp, client := p.get(w, nil, time.Second*30, u, headersKV)
		if resp == nil {
			return
		}
		defer resp.Body.Close()

		if parserBuilder, ok := parserdecoder.ParserBuilders.Get(parseKey); ok {
			var sorter common.Sorter
			var filter common.Filter
			var mapper common.Mapper
			var reader io.Reader
			reader = resp.Body
			if hasSorterKey || hasFilterKey {
				client2 := &http.Client{
					Transport: &http.Transport{
						TLSClientConfig: &tls.Config{
							InsecureSkipVerify: true,
						},
					},
					Timeout: time.Second * 20,
				}

				if u, err := url.Parse(u); err == nil {
					if u.Path != "/api/proxy" && u.Path != "/api/proxy/" {
						sorter, filter, mapper = proxyhelper.Get_sorted_filter_mapper(client2, u, fallbackPort, fallbackBasePath, hasSorterKey, hasFilterKey)
					}
				}

			}

			parser := parserBuilder(sorter, filter, mapper)

			utils.Logger.Debugf("url|url1=%s", u)
			for i := range 4 {
				key := fmt.Sprintf("url%d", i+2)
				u2 := r.Form.Get(key)
				// fmt.Printf("%d u2: %v\n", i, u2)
				if strings.HasPrefix(u2, "http") {
					utils.Logger.Debugf("%s=%s", key, u2)
					resp2, _ := p.get(nil, client, -1, u2, headersKV)
					if resp2 != nil {
						reader = io.MultiReader(reader, resp2.Body)
						defer resp2.Body.Close()
					}
				}
			}

			if parsed, err := parser.Parse(reader); err == nil {
				decoderBuilder, ok := parserdecoder.DecoderBuilders.Get(decodeKey)
				if !ok {
					decoderBuilder, ok = parserdecoder.DecoderBuilders.Get(defaultDecoderBuilderKey)
					if !ok {
						return
					}
				}

				decoder := decoderBuilder()
				strBuffer := &strings.Builder{}
				if err := decoder.Decode(strBuffer, parsed); err != nil {
					if len(history) > 10 {
						io.WriteString(w, history) //解析 错误, fallback: 返回 历史!
					}
					return
				}

				s := strBuffer.String()
				if r.Method == http.MethodGet {
					// cache: parse 过的值
					save(r.URL.String(), s)
					utils.Logger.Infof("cache save : %s, len=%d method=proxy.GET, url=%s", r.RemoteAddr, len(s), r.URL.String())
				}
				// 先 save, 再 write.
				io.WriteString(w, s)
				// io.Copy(w, reader)
				return
			}
		}
		return
	}

	resp, _ := p.get(w, nil, 0, u, headersKV)
	if resp == nil {
		return
	}
	defer resp.Body.Close()

	p.setHeaders(w, resp)

	if proxy_selector(w, resp) {
		return
	}

	// io.Copy(w, resp.Body)
	// bs := make([]byte, 1*1024*1024)
	// is_stream := len(r.Form.Get("stream")) > 0
	// if is_stream {
	// 	w.Header().Set("Transfer-Encoding", "chunked")
	// }
	// fmt.Println("=============> io.Copy()")
	// for {
	if _, err := io.Copy(w, resp.Body); err != nil {
		fmt.Printf("========================> err: %v\n", err)
		return
	}

	// if is_stream {
	// 	resp := p.get(w, client, u)
	// 	if resp == nil {
	// 		return
	// 	}
	// }

	// n, err := resp.Body.Read(bs)
	// if err != nil {
	// 	break
	// }
	// fmt.Printf("len(bs): %v\n", n)
	// if _, err := w.Write(bs[:n]); err != nil {
	// 	return
	// }
	// }

	// w2 := io.MultiWriter(w, os.Stdout)
	// io.Copy(w2, resp.Body)
	// io.Copy(w, resp.Body)
}

// for http response range

func (p *proxy) setHeaders(w http.ResponseWriter, resp *http.Response) {
	// fmt.Printf("resp.Status: %v\n", resp.Status)
	// fmt.Printf("resp.Header: %v\n", resp.Header)
	// fmt.Printf("w.Header().Get(\"Content-Type\"): %v,%v\n", resp.Header.Get("Content-Type"), u)

	// if strings.Contains(resp.Header.Get("Content-Type"), "text/plain") {
	// u2 := strings.ToLower(u)
	// if strings.HasSuffix(u2, ".m3u") || strings.HasSuffix(u2, ".m3u8") || strings.Contains(u2, ".m3u?") || strings.Contains(u2, ".m3u8?") {

	// }
	// }
	// 	w.Header().Add(utils.HeaderKeyContentType, resp.Header.Get(utils.HeaderKeyContentType))
	// 	p.setRange(w, resp)
	// }

	// func (p *proxy) setRange(w http.ResponseWriter, resp *http.Response) {
	//
	add := func(key string) bool {
		v := resp.Header.Get(key)
		if len(v) > 0 {
			w.Header().Set(key, v)
			return true
		}
		return false
	}
	//"Content-Type"
	add(utils.HeaderKeyContentType)
	//setRange
	add("Accept-Ranges")
	if add("Content-Range") {
		add("Content-Length")
	}
	// Range 返回: 206
	// w.WriteHeader(http.StatusPartialContent)

	w.WriteHeader(resp.StatusCode)
}

func (p *proxy) get(w http.ResponseWriter, client *http.Client, timeout time.Duration, u string, headers map[string]string) (*http.Response, *http.Client) {
	if client == nil {
		// resp, err := http.Get(u)
		client = &http.Client{
			Transport: p.tr,
			// Timeout:   timeout, // 视频流 不能断!!
		}

		// resp := p.get(w, client, u)
		// if resp == nil {
		// 	return
		// }

	}

	if timeout >= 0 {
		client.Timeout = timeout
	}

	req, err := http.NewRequest("GET", u, nil)
	if err != nil {
		return nil, client
	}

	if len(headers) > 0 {
		for k, v := range headers {
			utils.Logger.Debugf("key=%v value=%v", k, v)
			req.Header.Set(k, v)
		}
	}

	resp, err := client.Do(req) //client.Get(u)
	if err == nil {
		utils.Logger.Debugf("proxy resp.StatusCode=%v,URL=%v,query=%v", resp.StatusCode, resp.Request.URL, resp.Request.URL.RawQuery)
		return resp, client
	}

	if w == nil {
		return nil, client
	}

	utils.Logger.Debugf("proxy err:%v", err)
	parsedUrl, err := url.Parse(u)
	if err != nil {
		return nil, client
	}

	// 重定向到 静态文件
	fallbackProxy.Redirect(parsedUrl, client, fallbackPort, w)
	return nil, client
}

/*

// 1
	baseHost := "https://www.example.com/"
	client := &http.Client{
		CheckRedirect: func(req *http.Request, via []*http.Request) error {
		return http.ErrUseLastResponse
		},
	}

	res, err := client.Get(baseHost)
	if err != nil {
		return
	}

	if res.StatusCode != 301 {
		return baseHost
	}

	return res.Header.Get("Location")


*/

/*
// 2
type LogRedirects struct {
    Transport http.RoundTripper
}

func (l LogRedirects) RoundTrip(req *http.Request) (resp *http.Response, err error) {
    t := l.Transport
    if t == nil {
        t = http.DefaultTransport
    }
    resp, err = t.RoundTrip(req)
    if err != nil {
        return
    }
    switch resp.StatusCode {
    case http.StatusMovedPermanently, http.StatusFound, http.StatusSeeOther, http.StatusTemporaryRedirect:
        log.Println("Request for", req.URL, "redirected with status", resp.StatusCode)
    }
    return
}

*/

// encodeURIComponent(x)

// https://blog.csdn.net/a772304419/article/details/134355619
//// curl http://localhost:9966/api/proxy/?url=https%3A%2F%2Fblog.csdn.net%2Fa772304419%2Farticle%2Fdetails%2F134355619
// https://server.lan
// https%3A%2F%2Fserver.lan
//// curl http://localhost:9966/api/proxy/?url=https%3A%2F%2Fserver.lan
// http://server.lan:9966/
// http%3A%2F%2Fserver.lan%3A9966%2F
// curl http://localhost:9966/api/proxy/?url=http%3A%2F%2Fserver.lan%3A9966%2F

// https://gcalic.v.myalicdn.com/gc/wgw05_1/index.m3u8?contentid=2820180516001

// curl -I http://localhost:9966/api/proxy/?url=http%3A%2F%2Flocalhost%3A9966%2Fapi%2Fcode%2F302%3Fa%3Db

// curl -I "http://localhost:9966/api/proxy/?parser=m3u&url=http%3A%2F%2Flocalhost%3A9966%2Fapi%2Fcode%2F302%3Fa%3Db"
// curl http://localhost:9966/api/proxy/?parser=m3u&url=http://server22.lan:35455/tv.m3u
// http://server.lan:9966/api/proxy/?parser=m3u&url=http://server22.lan:35455/tv.m3u
// curl "http://server.lan:9966/api/proxy/?url=http://server22.lan:9966/box1_more_and_more/ok.live.txt"

// http://server.lan:9966/api/proxy/?parser=m3u&url=http://server22.lan:35455/tv.m3u
